package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.MISSING_DO;
import static wci.frontend.pascal.PascalErrorCode.MISSING_TO_DOWNTO;
import static wci.frontend.pascal.PascalTokenType.DO;
import static wci.frontend.pascal.PascalTokenType.DOWNTO;
import static wci.frontend.pascal.PascalTokenType.TO;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.VALUE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.ADD;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.ASSIGN;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.COMPOUND;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.GT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LOOP;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SUBTRACT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.TEST;

import java.util.EnumSet;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalErrorCode;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.TypeSpec;
import wci.intermediate.symtabimpl.Predefined;
import wci.intermediate.typeimpl.TypeChecker;
import wci.intermediate.typeimpl.TypeFormImpl;

/**
 * <h1>ForStatementParser</h1>
 * <p>解析Pascal FOR 语句</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class ForStatementParser extends StatementParser
{
    public ForStatementParser(PascalParserTD parent)
    {
        super(parent);
    }

    // TO和DOWNTO同步集合 for expr ->to|downto CONST do stmt
    private static final EnumSet<PascalTokenType> TO_DOWNTO_SET =
        ExpressionParser.EXPR_START_SET.clone();
    static {
        TO_DOWNTO_SET.add(TO);
        TO_DOWNTO_SET.add(DOWNTO);
        TO_DOWNTO_SET.addAll(StatementParser.STMT_FOLLOW_SET);
    }
    // DO同步集合for expr to|downto CONST ->do stmt
    private static final EnumSet<PascalTokenType> DO_SET =
        StatementParser.STMT_START_SET.clone();
    static {
        DO_SET.add(DO);
        DO_SET.addAll(StatementParser.STMT_FOLLOW_SET);
    }
    //发生的变量节点拷贝可以认为是源节点和拷贝节点树中关系不一样，但是一些属性
    //比如对应的符号表项还是一样的引用。
    public ICodeNode parse(Token token)
        throws Exception
    {
        token = nextToken(); 
        Token targetToken = token;
        //参见图7-11
        ICodeNode compoundNode = ICodeFactory.createICodeNode(COMPOUND);
        ICodeNode loopNode = ICodeFactory.createICodeNode(LOOP);
        ICodeNode testNode = ICodeFactory.createICodeNode(TEST);
        // 初始赋值语句，调用一次
        AssignmentStatementParser assignmentParser =
            new AssignmentStatementParser(this);
        ICodeNode initAssignNode = assignmentParser.parse(token);
        setLineNumber(initAssignNode, targetToken);
        TypeSpec controlType = initAssignNode != null
                ? initAssignNode.getTypeSpec()
                : Predefined.undefinedType;
        //for语句的初始变量一定得是一个整数或者枚举
		if (!TypeChecker.isInteger(controlType)
				&& (controlType.getForm() != TypeFormImpl.ENUMERATION)) {
			errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
		}
        compoundNode.addChild(initAssignNode);
        //循环主体的复合节点
        compoundNode.addChild(loopNode);
        //同步在TO/DOWNTO处
        token = synchronize(TO_DOWNTO_SET);
        TokenType direction = token.getType();
        if ((direction == TO) || (direction == DOWNTO)) {
            token = nextToken(); 
        }
        else {
            direction = TO;
            errorHandler.flag(token, MISSING_TO_DOWNTO, this);
        }
        //根据TO/DOWNTO创建判断表达式GT或LT
        ICodeNode relOpNode = ICodeFactory.createICodeNode(direction == TO
                                                           ? GT : LT);
        relOpNode.setTypeSpec(Predefined.booleanType); //关系默认为布尔类型
        //拷贝初始变量语句中的左边变量成为关系表达式的左边
        ICodeNode controlVarNode = initAssignNode.getChildren().get(0);
        relOpNode.addChild(controlVarNode.copy());
        //TO或DOWNTO的限值表达式，成为关系表达式的右边
        ExpressionParser expressionParser = new ExpressionParser(this);
        ICodeNode exprNode = expressionParser.parse(token);
		relOpNode.addChild(exprNode);
		TypeSpec exprType = exprNode != null ? exprNode.getTypeSpec()
                 : Predefined.undefinedType;
		//控制变量能够与表达式类型赋值兼容，不然无法复制
		if (!TypeChecker.areAssignmentCompatible(controlType, exprType)) {
			errorHandler.flag(token, PascalErrorCode.INCOMPATIBLE_TYPES, this);
		}
        //loop-->test-->relop三层
        testNode.addChild(relOpNode);
        loopNode.addChild(testNode);
        // 在DO处同步
        token = synchronize(DO_SET);
        if (token.getType() == DO) {
            token = nextToken(); 
        }
        else {
            errorHandler.flag(token, MISSING_DO, this);
        }
        //循环的主体语句
        StatementParser statementParser = new StatementParser(this);
        loopNode.addChild(statementParser.parse(token));
        //创建一个根据TO|DOWNTO来递增或递减控制变量的表达式。控制变量:=控制变量+/- 1;
        ICodeNode nextAssignNode = ICodeFactory.createICodeNode(ASSIGN);
        nextAssignNode.setTypeSpec(controlType);
        nextAssignNode.addChild(controlVarNode.copy());
        ICodeNode arithOpNode = ICodeFactory.createICodeNode(direction == TO
                                                             ? ADD : SUBTRACT);
        arithOpNode.setTypeSpec(Predefined.integerType);
        //递减发生在控制变量上，要拷贝。
        arithOpNode.addChild(controlVarNode.copy());
        ICodeNode oneNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
        oneNode.setAttribute(VALUE, 1);
        oneNode.setTypeSpec(Predefined.integerType);
        arithOpNode.addChild(oneNode);
        //将递增/递减后的值付给控制变量
        nextAssignNode.addChild(arithOpNode);
        loopNode.addChild(nextAssignNode);
        setLineNumber(nextAssignNode, targetToken);
        return compoundNode;
    }
}
